package com.xunk.scene.service;

import com.alibaba.fastjson.JSONObject;
import com.xunk.common.core.utils.BeanUtil;
import com.xunk.common.core.utils.HttpUtil;
import com.xunk.common.core.utils.StringUtils;
import com.xunk.scene.config.SceneConfig;
import com.xunk.scene.domain.TaskSchedule;
import com.xunk.scene.domain.XxlJobInfo;
import com.xunk.scene.mapper.XxlJobMapper;
import com.xunk.scene.util.CronUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @ProjectName: xunk-scene
 * @Package: com.xunk.job.service
 * @ClassName: XxlJobService
 * @Author: tangguangyuan
 * @Description: xxlJob 定时
 * @Date: 2021/4/22 14:28
 * @Version: 1.0
 */
@Service
public class XxlJobService {
    Logger log = LoggerFactory.getLogger(XxlJobService.class);

    @Autowired
    private SceneConfig sceneConfig;

    @Value("${crontab.url}")
    private String crontabExecAddress;

    @Autowired
    private XxlJobMapper xxlJobMapper;


    //添加定时任务到xxljob，如要添加的是只有天气的场景，无其他定时执行条件，isWeather需传true，否则传false
    public Boolean addCronToXxl(TaskSchedule taskSchedule, Long sceneId,boolean isWeather) {
        XxlJobInfo xxlJobInfo = setParam(taskSchedule, sceneId,isWeather);

        if (isWeather) {
            xxlJobInfo.setAuthor("WEATHER");
        }
        //将xxljobinfo对象转为map
        Map<String, String> paramMap = objectToMap(xxlJobInfo);

        //获取调度中心添加任务地址
        String url = sceneConfig.getXxlJobAddUrl();
        log.info("xxl地址:"+url);
        JSONObject response = HttpUtil.doPostForm(url, paramMap);
        Boolean result = doCheck(response);
        log.info("xxl-job返回"+response.toJSONString());
        return result;
    }

    //根据场景ID更新定时任务信息
    public Boolean updateCronToXxl(TaskSchedule taskSchedule, Long sceneId) {
        XxlJobInfo xxlJobInfo = setParam(taskSchedule, sceneId,false);

        //将xxljobinfo对象转为map
        Map<String, String> paramMap = objectToMap(xxlJobInfo);
        //获取调度中心更新任务地址
        String url = sceneConfig.getXxlJobUpdateUrl();
        log.info("xxl地址:"+url);
        JSONObject response = HttpUtil.doPostForm(url, paramMap);

        Boolean result = doCheck(response);

        log.info("xxl-job返回"+response.toJSONString());
        return result;
    }

    /**
     * 开启或关闭定时任务
     *
     * @param sceneId
     * @param activeFlag 0关闭 1开启
     * @return
     */
    public Boolean startOrStop(Long sceneId,String activeFlag) {
        if(StringUtils.isEmpty(activeFlag))
            log.error("未指定开启或关闭操作");
        //获取调度中心开启或停止的url
        String url = "0".equals(activeFlag)? sceneConfig.getXxlJobStopUrl():sceneConfig.getXxlJobStartUrl() ;
        boolean result = false;
        List<XxlJobInfo> xxlJobInfoList = xxlJobMapper.findJobInfoBySceneId(sceneId);

        for (XxlJobInfo xxlJobInfo :
                xxlJobInfoList) {
            int id = xxlJobInfo.getId();
            //定时任务ID放入param
            Map<String, String> param = new HashMap<String, String>() {{
                put("id", String.valueOf(id));
            }};

            JSONObject response = HttpUtil.doPostForm(url, param);
            result = doCheck(response);
        }
        return result;
    }

    /**
     * 根据场景ID批量删除定时任务
     * @param sceneId
     * @return
     */
    public Boolean removeBySceneId(Long sceneId){
        String url = sceneConfig.getXxlJobRemoveBySceneId();
        Map<String,String> param = new HashMap<String,String>(){{
            put("sceneId",String.valueOf(sceneId));
        }};

        JSONObject jsonObject = HttpUtil.doPostForm(url,param);
        boolean result = doCheck(jsonObject);
        return result;
    }


    //查询定时任务Cron表达式及作业类型,返回定时任务类
    public List<TaskSchedule> getTaskSchedule(Long sceneId) {
        List<XxlJobInfo> xxlJobInfoList = xxlJobMapper.findJobInfoBySceneId(sceneId);
        List<TaskSchedule> taskSchedules = new ArrayList<>();

        for (XxlJobInfo xxlJobInfo :
                xxlJobInfoList) {
            if (BeanUtil.isEmptyBean(xxlJobInfo)) {
                log.info(String.format("未查询到场景%s的定时任务", sceneId));
            } else {
                taskSchedules.add(CronUtil.buildTaskSchedule(xxlJobInfo.getScheduleConf(), xxlJobInfo.getJobType(),xxlJobInfo.getId())) ;
            }
        }
        return taskSchedules;
    }


    /**
     * 校验调度中心返回结果
     */
    private Boolean doCheck(JSONObject response) {
        if (response == null) {
            log.info("未接受到调度中心返回信息");
        }
        //判断调度中心返回结果
        Integer resultCode = (Integer) response.get("code");
        switch (resultCode) {
            case 200:
                return true;
            default:
                return false;
        }

    }

    //设置参数并返回xxljob实例
    public XxlJobInfo setParam(TaskSchedule taskSchedule, Long sceneId,boolean isWeather) {
        XxlJobInfo xxlJobInfo = new XxlJobInfo();
        //定时任务执行参数
        StringBuilder param = new StringBuilder(crontabExecAddress);
        param.append("?").append("sceneId=").append(sceneId).append("&").append("jobType=").append(taskSchedule.getJobType());
        //当添加的定时任务为天气默认的定时任务时，多传一个参数isWeather
        if(isWeather){
            param.append("&").append("isWeather=").append(isWeather);
        }
        param.append("\n");
        param.append("method:GET");
        param.append("\n");
        param.append("data:").append(sceneId);

        //将 定时 转换为cron表达式
        String cronExp = CronUtil.buildCronExpression(taskSchedule);

        //添加时定时任务时ID为空
        if(taskSchedule.getId()!=null)
            xxlJobInfo.setId(taskSchedule.getId());
        xxlJobInfo.setScheduleConf(cronExp);
        xxlJobInfo.setSceneId(sceneId);
        xxlJobInfo.setJobType(taskSchedule.getJobType());
        xxlJobInfo.setExecutorParam(param.toString());
        xxlJobInfo.setJobDesc(String.format("场景%d的定时任务",sceneId));

        return xxlJobInfo;
    }


    //xxljob对象转换为map
    public Map<String, String> objectToMap(XxlJobInfo xxlJobInfo) {
        Field[] fields = xxlJobInfo.getClass().getDeclaredFields();
        Map<String, String> map = new HashMap<>();
        Arrays.stream(fields).forEach(item -> {
            try {
                item.setAccessible(true);

                Object object = item.get(xxlJobInfo);
                if (object != null) {
                    map.put(item.getName(), object.toString());
                    System.out.println(object.toString());
                }

            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        });
        return map;
    }
}
